package com.baofu.paycore.biz.paycore.handler.impl;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.baofu.paycore.biz.engine.command.Command;
import com.baofu.paycore.biz.engine.command.impl.BalanceCommand;
import com.baofu.paycore.biz.engine.executor.CommandExecutor;
import com.baofu.paycore.biz.engine.generator.CommandGenerator;
import com.baofu.paycore.biz.engine.processor.impl.BalanceProcessor;
import com.baofu.paycore.biz.engine.result.CommandResult;
import com.baofu.paycore.biz.model.BackPayBO;
import com.baofu.paycore.biz.paycore.converter.BO2BOConverter;
import com.baofu.paycore.biz.paycore.converter.CommandConverter;
import com.baofu.paycore.biz.paycore.handler.BaseHandler;
import com.baofu.paycore.common.enums.FundTypeEnum;
import com.baofu.paycore.common.enums.UpdateUser;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.dal.model.PayDO;
import com.baofu.paycore.dal.model.SubPayDO;
import com.baofu.paycore.manager.model.PayBO;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.model.ResponseBO;
import com.baofu.paycore.manager.model.SubPayBO;
import com.baofu.paycore.manager.storage.impl.PayManager;
import com.baofu.paycore.manager.storage.impl.SubPayManager;
import com.baofu.paycore.manager.storer.PayOrderRepository;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.baofu.paycore.service.facade.enums.PayToolRouteEnum;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 消费订单
 *
 * <p/>
 *      1、支付订单处理
 * </p>
 * User: LZQ Date: 2016/04/20  Version: 1.0
 */
@Service
@Slf4j
public class ConsumeHandler extends BaseHandler<PayBO,RechargeBO>{

    /**
     * 持久服务
     */
    @Autowired
    private PayOrderRepository payOrderRepository;

    /**
     * 支付指令生成
     */
    @Resource(name = "payCommandGenerator")
    protected CommandGenerator<PayBO> payCommandGenerator;

    /**
     * 指令执行
     */
    @Resource
    protected CommandExecutor commandExecutor;

    /**
     * 转账服务
     */
    @Resource
    protected BalanceProcessor balanceProcessor;

    /**
     * 资金单Manager
     */
    @Autowired
    private SubPayManager subPayManager;

    /**
     * 支付主单服务
     */
    @Autowired
    private PayManager payManager;

    /**
     * 支付处理
     *
     * @param payBO 订单对象
     * @return BackPayBo
     */
    @Override
    public BackPayBO process(PayBO payBO) {
        log.info("开始处理：{}", payBO);
        doExecute(payBO);
        log.info("支付完成结果：{}",payBO);
        return BO2BOConverter.payBO2BackPayBO(payBO);
    }

    /**
     * 激活领域对象
     *
     * @param orderId 订单ID
     * @return 订单对象
     */
    @Override
    public PayBO activeOrder(String orderId) {
        PayBO payBO = payOrderRepository.load(orderId);
        if(payBO == null){
            log.error("找不到订单:{}",orderId);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }
        return payBO;
    }

    /**
     * 查询订单信息
     *
     * @param payNo   支付单号
     * @return        订单对象
     */
    @Override
    public PayBO queryOrderByPayNo(String payNo){

        return activeOrder(payNo);
    }

    /**
     * 前置检查
     *
     * @param order 订单
     */
    @Override
    protected void checkBeforeProcess(PayBO order) {
        order.checkStatus();
    }

    /**
     * 生成指令
     *
     * @param order 单据
     */
    @Override
    protected void processOrders(PayBO order) {
        List<Command> commands = payCommandGenerator.generatorCommands(order);
        Collections.sort(commands);

        log.info("支付指令, commands:{}", commands);

        Map<String, CommandResult> cmdResults = commandExecutor.executeCommand(commands);

        log.info("支付指令执行结果, cmdResults:{}",cmdResults);
        order.setRealFlag(true);
        processAfter(order, cmdResults);
    }

    /**
     * 持久化对象
     *
     * @param order 单据
     */
    @Override
    protected void reStore(PayBO order) {
        payOrderRepository.update(order);
    }

    /**
     * 指令完成后处理
     *
     * @param order     领域模型
     * @param cmdResult 指令处理结果
     */
    @Override
    protected void processAfter(PayBO order, Map<String, CommandResult> cmdResult) {
        //遍历所有资金单返回结果
        for (Map.Entry<String, CommandResult> each : cmdResult.entrySet()) {
            setFundDetailStatus(each, order);
        }
        finalCheck(order, cmdResult);
        log.info("支付主单状态status:{}", order.getStatus());
    }

    /**
     * 设置资金单状态
     *
     * @param entry     处理结果
     * @param payBO     支付订单
     */
    private void setFundDetailStatus(Map.Entry<String, CommandResult> entry,PayBO payBO) {
        SubPayBO subPayBO = payBO.getFundDetail(payBO.getSubPayList(),entry.getKey());
        CommandResult commandResult = entry.getValue();
        subPayBO.setTransReceiptNo(commandResult.getResponseNo());
        subPayBO.setTransDate(commandResult.getResponseDate());
        subPayBO.setErrorCode(commandResult.getResultCode());
        subPayBO.setErrorReason(commandResult.getResultMsg());

        if(!commandResult.isRealRtnFlag()){
            payBO.setErrorCode(null);
            subPayBO.setStatus(PayStatusEnums.INIT.getCode());
            return;
        }
        subPayBO.setStatus(commandResult.getStatus());
    }

    /**
     * 支付状态校验
     *
     * @param order         订单
     * @param cmdResult     处理结果集
     */
    private void finalCheck(PayBO order, Map<String, CommandResult> cmdResult){
        //出现未知的指令时将不会调用账务接口，更新单据状态为失败
        if (cmdResult.size() == 0 ) {
            //支付资金单中，只要有一单支付失败，主单为失败
            order.getSubPayList().forEach(fundDetail->fundDetail.setStatus(PayStatusEnums.FAIL.getCode()));
            order.setStatus(PayStatusEnums.FAIL.getCode());
            return;
        }

        for(CommandResult commandResult: cmdResult.values()){
            if(Objects.equals(PayStatusEnums.FAIL.getCode(), commandResult.getStatus())){
                order.setStatus(PayStatusEnums.FAIL.getCode());
                order.setErrorCode(commandResult.getResultCode());
                order.setErrorMsg(commandResult.getResultMsg());
                return;
            }
            if(!commandResult.isRealRtnFlag() ||
                    Objects.equals(PayStatusEnums.INIT.getCode(), commandResult.getStatus())){
                order.setErrorMsg(commandResult.getResultMsg());
                order.setErrorCode(commandResult.getResultCode());
                order.setStatus(PayStatusEnums.INIT.getCode());
                for(CommandResult result: cmdResult.values()) {
                    if(!Strings.isNullOrEmpty(result.getResultCode())){
                        order.setErrorCode(result.getResultCode());
                        order.setErrorMsg(result.getResultMsg());
                        return;
                    }
                }
                return;
            }
        }

        order.setStatus(PayStatusEnums.SUCCESS.getCode());

    }

    /**
     * 资金单转换map集合
     *
     * @param list  资金单
     * @return      map集合
     */
    private Map<String,SubPayBO> getSubPayMaps(List<SubPayBO> list){
        if (CollectionUtils.isEmpty(list)){
            log.error("支付 资金单为空");
            throw new PayCoreBizException(PayCoreErrorCode.EMPTY_QUERY_RESULT);
        }
        Map<String,SubPayBO> maps = Maps.newHashMap();
        for (SubPayBO subPayBO : list){
            maps.put(subPayBO.getFundType(), subPayBO);
        }

        return maps;
    }

    /**
     * 需重试接口 对内部资金单的重试（订单状态推进）
     *
     * @param payBO 支付订单对象
     * @return ResponseBO
     * @throws RuntimeException 出现异常时表示重试没有成功（子类方法实现中不可捕获异常）
     */
    public ResponseBO dalInnerFundProc(PayBO payBO) throws RuntimeException {
        log.info("重试主订单号:{} 资金单记录数:{}", payBO.getPayNo(), payBO.getSubPayList().size());
        ResponseBO responseBO = null;
        preDalInnerFund(payBO.getStatus(), payBO.getPayNo());
        //所有资金单
        Map<String,SubPayBO> maps = getSubPayMaps(payBO.getSubPayList());
        //先判断是否有银行卡支付单,有先处理卡支付单,再处理其他资金单,存在银行卡单,银行卡不成功不处理后面流程
        SubPayBO bankPay = maps.get(FundTypeEnum.PAY.getCode());
        if (bankPay != null && PayToolRouteEnum.CLEAR_WEBPAY_ROUTE.containsPayTool(bankPay.getPayToolType()) &&
                !PayStatusEnums.SUCCESS.getCode().equals(bankPay.getStatus())){
            log.error("银行单未处理成功 资金单号fundDetailNo:{} 状态:{}", bankPay.getFundDetailNo(), bankPay.getStatus());
            return null;
        }
        for (Map.Entry<String, SubPayBO> entry : maps.entrySet()) {
            SubPayBO subPayBO = entry.getValue();
            //成功资金单跳过
            if (PayStatusEnums.SUCCESS.getCode().equals(subPayBO.getStatus())){
                log.debug("请求服务类型:{} 资金单号:{} 资金单类型:{} 资金单状态:{}", payBO.getServiceCode(),
                        subPayBO.getFundDetailNo(), subPayBO.getFundType(), subPayBO.getStatus());
                continue;
            }
            //有失败直接返回
            if (PayStatusEnums.FAIL.getCode().equals(subPayBO.getStatus())){
                log.error("请求服务类型:{} 资金单号:{} 资金单类型:{} 资金单状态:{}", payBO.getServiceCode(),
                        subPayBO.getFundDetailNo(), subPayBO.getFundType(), subPayBO.getStatus());
                break;
            }
            //转账
            BalanceCommand balanceCommand = CommandConverter.balanceCommandConverter(subPayBO, payBO);
            Map<String, CommandResult> resultMap = balanceProcessor.doProcess(balanceCommand);
            CommandResult result = resultMap.get(subPayBO.getFundDetailNo());
            responseBO = handleResult(subPayBO, result);
            if (!validateContinue(result.isRealRtnFlag(), result.getStatus())){
                log.error("重试 转账失败 请求服务类型:{} 资金单号:{} 资金单类型:{} 转账处理状态:{}", payBO.getServiceCode(),
                        subPayBO.getFundDetailNo(), subPayBO.getFundType(), result.getStatus());
                break;
            }
        }
        handlerPayStatus(responseBO, payBO);

        return responseBO;
    }

    /**
     * 处理资金单
     *
     * @param subPayBO  子资金单
     * @param result    结果
     * @return          ResponseBO
     */
    protected ResponseBO handleResult(SubPayBO subPayBO, CommandResult result){

        SubPayDO subPayDO = new SubPayDO();
        subPayDO.setFundDetailNo(subPayBO.getFundDetailNo());
        subPayDO.setStatus(result.getStatus());
        subPayDO.setErrorCode(result.getResultCode());
        subPayDO.setErrorReason(result.getResultMsg());
        subPayDO.setTransDate(result.getResponseDate());
        subPayDO.setTransReceiptNo(result.getResponseNo());
        subPayDO.setUpdatedBy(UpdateUser.SYS.getUserName());
        //更新单
        subPayManager.modifyOrder(subPayDO);

        return new ResponseBO(result.getStatus(), result.getResultCode(), result.getResultMsg(),
                result.getResponseDate(), result.getResponseNo(), subPayBO.getFundDetailNo(),
                result.isRealRtnFlag());
    }

    /**
     * 处理支付主单
     *
     * @param responseBO    响应结果对象
     * @param payBO         支付单
     */
    private void handlerPayStatus(ResponseBO responseBO, PayBO payBO){
        if (!PayStatusEnums.SUCCESS.getCode().equals(responseBO.getStatus())){
            return;
        }
        PayDO payDO = new PayDO();
        payDO.setPayNo(payBO.getPayNo());
        payDO.setStatus(responseBO.getStatus());
        payManager.modifyOrder(payDO);
    }
}
